package com.ls.aicoderbackend.core;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONUtil;
import com.ls.aicoderbackend.ai.AICodeGeneratorServiceFactory;
import com.ls.aicoderbackend.ai.AiCodeGeneratorService;
import com.ls.aicoderbackend.ai.model.HtmlCodeResult;
import com.ls.aicoderbackend.ai.model.MultiFileCodeResult;
import com.ls.aicoderbackend.ai.model.message.AiResponseMessage;
import com.ls.aicoderbackend.ai.model.message.ToolExecutedMessage;
import com.ls.aicoderbackend.ai.model.message.ToolRequestMessage;
import com.ls.aicoderbackend.constant.AppConstant;
import com.ls.aicoderbackend.core.builder.VueProjectBuilder;
import com.ls.aicoderbackend.core.parser.CodeParserExecutor;
import com.ls.aicoderbackend.core.saver.CodeFileSaveExecutor;
import com.ls.aicoderbackend.exception.BusinessException;
import com.ls.aicoderbackend.exception.ErrorCode;
import com.ls.aicoderbackend.exception.ThrowUtils;
import com.ls.aicoderbackend.model.enums.CodeGenTypeEnum;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.tool.ToolExecution;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;

@Service
@Slf4j
public class AiCodeGeneratorFacade {

    @Resource
    private AICodeGeneratorServiceFactory aiCodeGeneratorServiceFactory;

    @Resource
    private VueProjectBuilder vueProjectBuilder;

    /**
     * 生成并保存代码
     * @param useMessage
     * @param codeGenTypeEnum
     * @param appId
     * @return
     */
    public File generateAndSaveCode(String useMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
        ThrowUtils.throwIf(ObjUtil.isNull(codeGenTypeEnum), ErrorCode.SYSTEM_ERROR, "代码生成类型不能为空");
        AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAiCodeGeneratorService(appId);
        return switch (codeGenTypeEnum) {
            case HTML -> {
                HtmlCodeResult htmlCodeResult = aiCodeGeneratorService.generateHtmlCode(useMessage);
                yield CodeFileSaveExecutor.saveCodeFile(htmlCodeResult, codeGenTypeEnum, appId);
            }
            case MULTI_FILE -> {
                MultiFileCodeResult multiFileCodeResult = aiCodeGeneratorService.generateMultiFileCode(useMessage);
                yield CodeFileSaveExecutor.saveCodeFile(multiFileCodeResult, codeGenTypeEnum, appId);
            }
            default -> {
                String errorMsg = "不支持的生成类型";
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMsg);
            }
        };
    }

    /**
     * 生成并保存代码 （流式）
     * @param useMessage
     * @param codeGenTypeEnum
     * @param appId
     * @return
     */
    public Flux<String> generateAndSaveCodeStream(String useMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
        ThrowUtils.throwIf(ObjUtil.isNull(codeGenTypeEnum), ErrorCode.SYSTEM_ERROR, "代码生成类型不能为空");
        AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAiCodeGeneratorService(appId, codeGenTypeEnum);
        return switch (codeGenTypeEnum) {
            case HTML -> {
                Flux<String> stringFlux = aiCodeGeneratorService.generateHtmlCodeStream(useMessage);
                yield processCodeStream(stringFlux, CodeGenTypeEnum.HTML, appId);
            }
            case MULTI_FILE -> {
                Flux<String> stringFlux = aiCodeGeneratorService.generateMultiFileCodeStream(useMessage);
                yield processCodeStream(stringFlux, CodeGenTypeEnum.MULTI_FILE, appId);
            }
            case VUE_PROJECT -> {
                TokenStream tokenStream = aiCodeGeneratorService.generateVueProjectCodeStream(appId, useMessage);
                yield processTokenStream(tokenStream, appId);
            }
            default -> {
                String errorMsg = "不支持的生成类型";
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMsg);
            }
        };
    }

    /**
     * 处理token流，转化为Flux<String>
     * @param tokenStream
     * @return
     */
    private Flux<String> processTokenStream(TokenStream tokenStream, Long appId) {
        return Flux.create(sink -> {
            // 处理AI部分响应数据
            tokenStream.onPartialResponse((String partialResponse) -> {
                        AiResponseMessage aiResponseMessage = new AiResponseMessage(partialResponse);
                        sink.next(JSONUtil.toJsonStr(aiResponseMessage));
                    })
                    // 处理AI调用工具请求
                    .onPartialToolExecutionRequest((index, toolExecutionRequest) -> {
                        ToolRequestMessage toolRequestMessage = new ToolRequestMessage(toolExecutionRequest);
                        sink.next(JSONUtil.toJsonStr(toolRequestMessage));
                    })
                    // 处理AI调用工具执行结果
                    .onToolExecuted((ToolExecution toolExecution) -> {
                        ToolExecutedMessage toolExecutedMessage = new ToolExecutedMessage(toolExecution);
                        sink.next(JSONUtil.toJsonStr(toolExecutedMessage));
                    })
                    // 处理响应完成事件
                    .onCompleteResponse((ChatResponse chatResponse) -> {
                        // 同步构建项目
                        String projectPath = AppConstant.CODE_OUTPUT_ROOT_DIR + "/vue_project_" + appId;
                        vueProjectBuilder.buildProjectAnsy(projectPath);
                        sink.complete();
                    })
                    // 处理错误事件
                    .onError((Throwable error) -> {
                        log.error(error.getMessage());
                        sink.error(error);
                    })
                    .start();
        });
    }

    // 生成文件并保存 （流式）
    private Flux<String> processCodeStream(Flux<String> codeStream, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
        StringBuilder codeBuilder = new StringBuilder();
        return codeStream.doOnNext(codeBuilder::append).doOnComplete(() -> {
            try {
                String codeResult = codeBuilder.toString();
                // 解析最终组合的文件
                Object parserResult = CodeParserExecutor.executeParser(codeResult, codeGenTypeEnum);
                // 将文件保存写入到文件夹中
                File saveCodeFile = CodeFileSaveExecutor.saveCodeFile(parserResult, codeGenTypeEnum, appId);
                log.info("生成成功, 路径为{}", saveCodeFile.getAbsolutePath());
            } catch (Exception e) {
                log.error("生成失败: {}", e.getMessage());
            }
        });
    }
}
